#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os
import json

from common.common import check_command_injection
from common.const import RepositoryDataTypeEnum, BackupTypeEnum, ExecuteResultEnum
from common.exception.common_exception import ErrCodeException
from informix import LOGGER
from informix.comm.comm import get_local_uuid, check_path_legal, check_path_user, check_log_backup_path, \
    identify_env_user
from informix.comm.const import JsonConstant, RegisterType, FileConstant, ErrorCode, InformixInfo, \
    InformixInstanceStatus, DeployType, MountType
from informix.job_manager import JobManager
from informix.param.parse_params import BaseParam


class InformixParseBackupParam(BaseParam):
    """
    备份入参json文件的解析
    """

    def __init__(self, json_params, job_manager: JobManager):
        super(InformixParseBackupParam, self).__init__(json_params, job_manager, LOGGER)
        self.host_uuid_curr = ""
        self.custom_para_curr = ""
        self.copy_id = ""
        self.job = json_params.get(JsonConstant.JOB, {})
        self.protect_env = self.job.get(JsonConstant.PROTECT_ENV, {})
        self.protect_object = self.job.get(JsonConstant.PROTECT_OBJECT, {})
        self._mount_type = self.job.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.AGENT_MOUNT_TYPE, '')
        self.nodes = self.protect_env.get(JsonConstant.NODES, [])
        self.protect_env_extend_info = self.protect_env.get(JsonConstant.EXTEND_INFO, {})
        self.protect_object_extend_info = self.protect_object.get(JsonConstant.EXTEND_INFO, {})
        self.is_cluster = self._get_single_or_cluster() == RegisterType.CLUSTER
        self.db_user = identify_env_user() if identify_env_user() else InformixInfo.INFORMIX_USER
        self.deploy_type = self.protect_env.get("extendInfo", {}).get("deployType", 0)

    @staticmethod
    def get_base_copy_id(copy_info: dict):
        """
        从备份时上报的副本信息中获取基备份副本id
        """
        base_copy_id = copy_info.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.BASE_COPY_ID)
        return base_copy_id

    @staticmethod
    def _get_target_value(is_match, json1, json2, key):
        return json1.get(key, "") if is_match else json2.get(key, "")

    def get_protect_object(self):
        return self.protect_object

    def get_server_num(self):
        if not self.is_cluster:
            return self.protect_object_extend_info.get(JsonConstant.SERVER_NUM, "")
        node = self.get_curr_node()
        if not node:
            return ''
        return node.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.SERVER_NUM, '')

    def get_backup_job_result(self):
        return self._json_param.get(JsonConstant.BACK_JOB_RESULT, 0)

    def get_job_repositories(self):
        repositories = self._json_param.get(JsonConstant.JOB, {}).get(JsonConstant.REPOSITORIES, [])
        return repositories

    def get_backup_type(self):
        """
        获取备份类型：全量/增量/差异
        """
        return self._json_param.get(JsonConstant.JOB, {}). \
            get(JsonConstant.JOB_PARAM, {}).get(JsonConstant.BACKUP_TYPE, 0)

    def get_nodes_info(self):
        """
        获取所有节点信息
        :return:
        """
        return self._json_param.get(JsonConstant.JOB, {}). \
            get(JsonConstant.PROTECT_ENV, {}).get(JsonConstant.NODES)

    def get_protectenv_extendinfo_cluster_info(self):
        """
        获取portectEnv下extendInfo下的cluster_info信息
        """
        cluster_info = self._json_param.get(JsonConstant.JOB, {}). \
            get(JsonConstant.PROTECT_ENV, {}).get(JsonConstant.EXTEND_INFO, {}).get(
            JsonConstant.CLUSTER_INFO)
        cluster_info = json.loads(cluster_info)
        return cluster_info

    def get_copys(self):
        """
        获取所有的副本信息
        """
        return self._json_param.get(JsonConstant.JOB, {}). \
            get(JsonConstant.COPY, {})

    def get_curr_node(self):
        host_uuid = get_local_uuid()
        if not host_uuid:
            return {}
        nodes = self.protect_env.get(JsonConstant.NODES, [])
        if not nodes:
            return {}
        for node in nodes:
            if host_uuid == node.get(JsonConstant.ID, ''):
                return node
        return {}

    def get_sqlhosts_file_curr(self):
        """
        获取当前节点的sqlhost文件路径
        """
        if not self.is_cluster:
            return self.protect_object_extend_info.get(JsonConstant.SQLHOSTS_PATH, '')
        node = self.get_curr_node()
        if not node:
            return ''
        sqlhosts_file = node.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.SQLHOSTS_PATH, '')
        if sqlhosts_file:
            if check_command_injection(sqlhosts_file) or \
                    not check_path_user(sqlhosts_file, self.db_user, self.db_user):
                LOGGER.error("Failed to verify the parameter(sqlhosts_file) validity.")
                raise ErrCodeException(ErrorCode.ERROR_PARAM.value)
        return sqlhosts_file

    def get_onconfig_file_curr(self):
        """
        获取当前节点的onconfig文件路径
        """
        if not self.is_cluster:
            return self.protect_object_extend_info.get(JsonConstant.ONCONFIG_PATH, '')
        node = self.get_curr_node()
        if not node:
            return ''
        onconfig_file = node.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.ONCONFIG_PATH, '')
        if onconfig_file:
            if check_command_injection(onconfig_file) or \
                    not check_path_user(onconfig_file, self.db_user, self.db_user):
                LOGGER.error("Failed to verify the parameter(onconfig_file) validity.")
                raise ErrCodeException(ErrorCode.ERROR_PARAM.value)
        return onconfig_file

    def get_onconfig_file_name_curr(self):
        """
        获取当前节点的onconfig文件名称
        """
        file_name = self.get_onconfig_file_curr()
        onconfig_file_name = file_name.split('/')[-1]
        if onconfig_file_name and check_command_injection(onconfig_file_name):
            LOGGER.error("Failed to verify the parameter(onconfig_file_name) validity.")
            raise ErrCodeException(ErrorCode.ERROR_PARAM.value)
        return onconfig_file_name

    def get_instance_name_curr(self):
        """
        获取当前节点的实例名称
        """
        if not self.is_cluster:
            return self.protect_object.get(JsonConstant.NAME, '')
        node = self.get_curr_node()
        if not node:
            return ''
        instance_name_curr = node.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.LOCALSERVER, '')
        if instance_name_curr and check_command_injection(instance_name_curr):
            LOGGER.error("Failed to verify the parameter(instance_name_curr) validity.")
            raise ErrCodeException(ErrorCode.ERROR_PARAM.value)
        return instance_name_curr

    def get_rootdbs_path_curr(self):
        """
        获取当前节点的rootdbs路径信息
        """
        if not self.is_cluster:
            return self.protect_object.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.ROOTDBS_PATH, '')
        node = self.get_curr_node()
        if not node:
            return ''
        return node.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.ROOTDBS_PATH, '')

    def get_log_backup_status(self):
        """
        获取日志备份开启标识
        由于使用此函数之前已经判断当前节点是否为主节点，故此处直接检查主节点是否开启日志备份
        """
        status = '0'  # 默认状态为关闭，0：关闭 1：打开
        if self.deploy_type == DeployType.SINGLE:
            LOGGER.info("single node instance scene")
            status = self.protect_env.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.LOG_BACKUP, '')
        elif self.deploy_type == DeployType.AP:
            for node in self.protect_env.get('nodes', {}):
                node_info = node.get(JsonConstant.EXTEND_INFO, {})
                if node_info.get(JsonConstant.INSTANCE_STATUS, '') == \
                        InformixInstanceStatus.ON_LINE_CLUSTER_MASTER_NODE[0]:
                    status = node_info.get('logBackup', '')
        return True if status == '1' else False

    def get_log_path(self):
        """
        获取日志仓路径
        :return:
        """
        log_path = self.get_designated_path(RepositoryDataTypeEnum.LOG_REPOSITORY.value)
        if log_path and check_command_injection(log_path):
            LOGGER.error("Failed to verify the parameter(log_path) validity.")
            raise ErrCodeException(ErrorCode.ERROR_PARAM.value)
        return log_path

    def get_local_log_backup_path_gbase(self):
        """
        由于GBase8s存在资源注册后主节点不在资源信息第一位情况，单独获取主节点日志备份信息
        """
        log_backup_path = ''
        if self.deploy_type == DeployType.SINGLE:
            log_backup_path = self.protect_env.get("extendInfo", {}).get("logBackupPath")
        elif self.deploy_type == DeployType.AP:
            for node in self.protect_env.get('nodes', {}):
                node_info = node.get('extendInfo', {})
                if node_info.get('instanceStatus', '') == InformixInstanceStatus.ON_LINE_CLUSTER_MASTER_NODE[0]:
                    log_backup_path = node_info.get("logBackupPath")

        return log_backup_path

    def get_local_log_backup_path(self, db_user=InformixInfo.INFORMIX_USER):
        """
        获取本地日志备份路径
        :return:
        """
        log_backup_path = self.protect_env.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.LOG_BACKUP_PATH, '')
        if db_user != InformixInfo.INFORMIX_USER:
            log_backup_path = self.get_local_log_backup_path_gbase()
        if log_backup_path and check_command_injection(log_backup_path):
            LOGGER.error("Failed to verify the parameter(log_backup_path) validity.")
            raise ErrCodeException(ErrorCode.ERROR_PARAM.value)
        if log_backup_path:
            error_code = check_log_backup_path(db_user, log_backup_path)
            if error_code != ExecuteResultEnum.SUCCESS:
                LOGGER.error("The log backup path does not exist or dose not have permission")
                raise ErrCodeException(ErrorCode.ERR_LOG_BACKUP_PATH.value)
        return log_backup_path

    def get_log_repository(self):
        """
        获取本地日志仓
        :return:
        """
        repositories = self.get_job_repositories()
        if not repositories:
            self._log_obj.error(f"Repositories is null.{self.get_log_common()}")
            return {}
        for repository in repositories:
            if repository.get(JsonConstant.REPOSITORY_TYPE, "") == 3:
                return repository
        return {}

    def get_log_remotepath_ip(self):
        path_, ip = '', ''
        repositories = self.get_job_repositories()
        if not repositories:
            self._log_obj.error(f"Repositories is null.{self.get_log_common()}")
            return path_, ip
        for repository in repositories:
            if repository.get(JsonConstant.REPOSITORY_TYPE, "") != 3:
                continue
            path_ = repository.get(JsonConstant.REMOTE_PATH, '')
            if not path_:
                raise KeyError()
            remote_host = repository.get(JsonConstant.REMOTE_HOST, [])
            if not remote_host:
                raise KeyError()
            ip = remote_host[0].get(JsonConstant.IP)
            if not ip:
                raise KeyError()
        return path_, ip

    def get_meta_path(self):
        backup_type = self.get_backup_type()
        sign = "/meta/"
        if backup_type == BackupTypeEnum.LOG_BACKUP:
            sign = '/log/'
        repositories = self.get_job_repositories()
        if not repositories:
            return ""
        for repository in repositories:
            if repository.get(JsonConstant.REPOSITORY_TYPE, "") != RepositoryDataTypeEnum.META_REPOSITORY.value:
                continue
            path_array = repository.get(JsonConstant.PATH, [])
            if len(path_array) <= 0 or not path_array[0]:
                continue
            # 本地盘环境直接返回第一个合法路径
            if self._mount_type == MountType.FUSE:
                if check_path_legal(path_array[0], FileConstant.INFORMIX_FILESYSTEM_MOUNT_PATH):
                    self._log_obj.info(f"META PATH:{path_array[0]} {self.get_log_common()}")
                    return path_array[0]
            # 返回取到得路径中的第一个符合合法性校验的
            for path in path_array:
                if sign not in path:
                    continue
                if check_path_legal(path, FileConstant.INFORMIX_FILESYSTEM_MOUNT_PATH):
                    self._log_obj.info(f"META PATH:{path} {self.get_log_common()}")
                    return path
        return ""

    def get_resource_id(self):
        result = self.protect_object.get(JsonConstant.ID, '')
        return result

    def get_job_extend_info(self):
        return self.job.get(JsonConstant.EXTEND_INFO, {})

    def is_san_client_involved(self):
        extend_info = self.get_job_extend_info()
        return extend_info.get('sanclientInvolved') == 'true'

    def _get_single_or_cluster(self):
        result = self.protect_object.get(JsonConstant.SUB_TYPE, '')
        if result == JsonConstant.INFORMIX_SINGLE_INSTANCE:
            return RegisterType.SINGLE
        elif result == JsonConstant.INFORMIX_CLUSTER_INSTANCE:
            return RegisterType.CLUSTER
        return RegisterType.ERR_TYPE
